#include <iostream>

#include "predictors/predictor_link.h"
#include "model_link.h"
#include "model.h"
#include "machine.h"
#include "predictors/predictor_base.h"
#include "ioconnections.h"
#include "output_base.h"

using namespace std;

Connection::Connection()
{
}

Connection::~Connection()
{
}

void Connection::set_name(std::string name)
{
  _name = name;
}

std::string Connection::get_name(void)
{
  return _name;
}

void Connection::set_id(int id)
{
  _id = id;
}

int Connection::get_id(void)
{
  return _id;
}

IConnection::IConnection(Machine *m, Predictor_base *p, int iLink) :
  Connection()
{
  _value_p = NULL;
  _link = p->get_input_link(iLink);
  m->add_input_connection(this);
}

IConnection::IConnection(Predictor_link *l) :
  Connection()
{
  _link = l;
}

IConnection::~IConnection()
{
}

void IConnection::set_value_p(float *p)
{
  _value_p = p;
}

float *IConnection::get_value_p(void)
{
  return _value_p;
}

void IConnection::set_link(Predictor_link *l)
{
  _link = l;
}

void IConnection::get_value()
{
  if (_value_p == NULL) {
    cout << "ERROR: value_p set to NULL" << endl;
    exit(1);
  }
  if (_link->get_link_type() != INPUT) {
    cout << "ERROR: link is not of input type" << endl;
    exit(1);
  }
  _link->set_value(*_value_p);
}

OConnection::OConnection(Machine *m, Model *model) :
  Connection()
{
  _model_link = model->get_output_link();
  m->add_output_connection(this);
}

OConnection::OConnection(Model_link *l) :
  Connection()
{
  _model_link = l;
  _output = NULL;
}

OConnection::~OConnection()
{
}

void OConnection::set_link(Model_link *l)
{
  _model_link = l;
}

void OConnection::set_function(boost::function<void(float)> f)
{
  _out_function = f;
}

// returns true if output value was set
void OConnection::set_value()
{
  if (!_out_function) {
    cout << "ERROR: no target. _out_function = " << _out_function << endl;
    exit(1);
  }

  if (_model_link == NULL) {
    cout << "ERROR: model_link set to NULL" << endl;
    exit(1);
  }

  if (_model_link->is_active()) {
    _out_function(_output->get_output_value());
  }
}

Output_base *OConnection::get_output(void)
{
  return _output;
}

void OConnection::set_output(Output_base *output)
{
  _output = output;
}

double OConnection::get_output_value()
{
  if (_output == NULL) {
    cout << "ERROR: No output defined in output connection" << endl;
    exit(1);
  }
  return _output->get_output_value();
}

bool OConnection::is_active()
{
  return _model_link->is_active();
}
